/* An animated sprite, not written by us. */

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using RoboTrocity;
using System.Collections.Generic;

namespace RoboTrocity {
    
    public class Sprite
    {
        private string imgLoc;
        private int framecount;
        private Texture2D texture;
        private float timePerFrame;
        private int frame;
        private float TotalElapsed;
        private bool Paused;
        public float Rotation, Scale;
        public Vector2 Origin;
		List<CollisionBox>[] collisionBoxes; 
		//each block of the array represents a frame of the sprite's animation. Each List holds all the collision boxes
		//associated with that frame

		/// <summary>
		/// This constructor uses the _imgLoc parameter to get the Texture from the GraphicsDispenser, if possible.
		/// </summary>
		/// <param name="_imgLoc">The Asset Name of the texture (no path)</param>
        public Sprite(string _imgLoc):
			this(_imgLoc, 1, 1){
		}
		/// <summary>
		/// Call this constructor if you're creating the sprite after the graphics have been loaded. 
		/// Use GraphicsDispenser.getTexture(string) to get a pre-loaded Texture2D
		/// </summary>
		/// <param name="_texture">A texture from GraphicsDispenser</param>
		private Sprite(Texture2D _texture)
			: this(_texture, 1, 1) {}

		/// <summary>
		/// This constructor uses the _imgLoc parameter to get the Texture from the GraphicsDispenser, if possible.
		/// </summary>
		/// <param name="_imgLoc">The Asset Name of the texture (no path)</param>
		/// <param name="_frameCount">The number of frames of animation</param>
		/// <param name="_framesPerSec">How many frames pass per second of animation</param>
        public Sprite(string _imgLoc, int _frameCount, int _framesPerSec):this(Vector2.Zero, 0.0f, 1.0f){
			if (GraphicsDispenser.getTexture(_imgLoc) != null) {
				texture = GraphicsDispenser.getTexture(_imgLoc);
			}
			framecount = _frameCount;
            timePerFrame = (float)1 / _framesPerSec;
            imgLoc = _imgLoc;
			collisionBoxes = new List<CollisionBox>[framecount];
			initCollisionBoxes();
        }

		/// <summary>
		/// Call this constructor if you're creating the sprite after the graphics have been loaded. 
		/// Use GraphicsDispenser.getTexture(string) to get a pre-loaded Texture2D
		/// </summary>
		/// <param name="_texture">A texture from GraphicsDispenser</param>
		/// <param name="_frameCount">The number of frames of animation</param>
		/// <param name="_framesPerSec">How many frames pass per second of animation</param>
        private Sprite(Texture2D _texture, int _frameCount, int _framesPerSec): this(Vector2.Zero, 0.0f, 1.0f) {
			texture = _texture;
			framecount = _frameCount;
			timePerFrame = (float)1 / _framesPerSec;
			frame = 0;
			TotalElapsed = 0;
			collisionBoxes = new List<CollisionBox>[framecount];
			initCollisionBoxes();
		}

		/// <summary>
		/// An initial constructor for Sprites. It just sets a few parameters, but is called by every other constructor.
		/// </summary>
		/// <param name="Origin">Where the origin of the Sprite is. Keep at Vector2.Zero</param>
		/// <param name="Rotation">The rotation of the Sprite. 0.0 is default.</param>
		/// <param name="Scale">Scale of the sprite. 1.0 is normal.</param>
		private Sprite(Vector2 Origin, float Rotation, float Scale) {
			this.Origin = Origin;
			this.Rotation = Rotation;
			this.Scale = Scale;
			frame = 0; TotalElapsed = 0;
			Paused = false;
		}

		/// <summary>
		/// This initializes all the collision boxes within the Sprite.
		/// </summary>
		public void initCollisionBoxes() {
			for (int i = 0; i < framecount; i++) {
				collisionBoxes[i] = new List<CollisionBox>();
			}
		}

		/// <summary>
		/// Adds a collision box to the Sprite at a certain frame.
		/// </summary>
		/// <param name="newBox">The CollisionBox to add.</param>
		/// <param name="frameNum">The frame of animation at which to add the CollisionBox.</param>
		public void addCollisionBox(CollisionBox newBox, int frameNum) {
			if (frameNum > framecount || frameNum < 1) { return; }
			collisionBoxes[frameNum-1].Add(newBox);
		}

		public List<CollisionBox> CollisionBoxes(CollisionBox.BoxType desiredType) {
			List<CollisionBox> boxes = new List<CollisionBox>();
			foreach (CollisionBox box in collisionBoxes[frame]) {
				if (box.Type == desiredType) {
					boxes.Add(box);
				}
			}
			return  boxes;
		}

		/// <summary>
		/// Updates the frame based on the time that has passed.
		/// </summary>
		/// <param name="elapsed">Time elapsed since last tick.</param>
        public void UpdateFrame(float elapsed) {

            if (Paused) { return; }

            TotalElapsed += elapsed;

            if (TotalElapsed > timePerFrame) {
                frame++;

                // Keep the Frame between 0 and the total frames, minus one.
                frame = frame % framecount;
                TotalElapsed -= timePerFrame;
            }
        }

		/// <summary>
		/// Draws the current frame of the sprite to the screen.
		/// </summary>
		/// <param name="Batch">The SpriteBatch to use.</param>
		/// <param name="screenpos">Where on the screen to draw the Sprite.</param>
		/// <param name="depth">How deep in the screen to draw the sprite (0.0 to 1.0)</param>
        public void DrawFrame(SpriteBatch Batch, RoboTrocity.Point screenpos, float depth) {
            DrawFrame(Batch, screenpos, depth, false);
        }

		/// <summary>
		/// Draws the current frame of the sprite to the screen
		/// </summary>
		/// <param name="Batch">The SpriteBatch to use.</param>
		/// <param name="screenpos">Where on the screen to draw the Sprite.</param>
		/// <param name="depth">How deep in the screen to draw the sprite (0.0 to 1.0)</param>
		/// <param name="flipHorizontal">Whether or not to flip the sprite horizontally</param>
		public void DrawFrame(SpriteBatch Batch, RoboTrocity.Point screenpos, float depth, bool flipHorizontal) {
			DrawFrame(Batch, frame, screenpos, depth, flipHorizontal);
        }

		/// <summary>
		/// Draws the current frame of the sprite to the screen.
		/// </summary>
		/// <param name="Batch">The SpriteBatch to use.</param>
		/// <param name="frameDraw">The frame to draw to the screen.</param>
		/// <param name="screenpos">Where on the screen to draw the Sprite.</param>
		/// <param name="depth">How deep in the screen to draw the sprite (0.0 to 1.0)</param>
		/// <param name="flipHorizontal">Whether or not to flip the sprite horizontally</param>
		public void DrawFrame(SpriteBatch Batch, int frameDraw, RoboTrocity.Point screenpos, float depth, bool flipHorizontal) {
			int FrameWidth = texture.Width / framecount;
			Rectangle sourcerect = new Rectangle(FrameWidth * frameDraw, 0,	FrameWidth, texture.Height);
			Vector2 screenPosVector = new Vector2(screenpos.X, screenpos.Y);
			if (!flipHorizontal){
				Batch.Draw(texture, screenPosVector, sourcerect, Color.White,
								Rotation, Origin, Scale, SpriteEffects.None, depth);
			} else {
				Batch.Draw(texture, screenPosVector, sourcerect, Color.White,
								Rotation, Origin, Scale, SpriteEffects.FlipHorizontally, depth);
			}
		}

		public void DrawFrame(SpriteBatch Batch, Rectangle destRect, float Depth, bool flipHor) {
			int FrameWidth = texture.Width / framecount;
			Rectangle sourcerect = new Rectangle(FrameWidth * Frame, 0, FrameWidth, texture.Height);
			if (!flipHor) {
				Batch.Draw(texture, destRect, sourcerect, Color.White, Rotation, Origin, SpriteEffects.None, Depth);
			} else {
				Batch.Draw(texture, destRect, sourcerect, Color.White, Rotation, Origin, SpriteEffects.FlipHorizontally, Depth);	
			}
		}
		/// <summary>
		/// Checks to see if the Sprite is paused.
		/// </summary>
		public bool IsPaused {
            get { return Paused; }
        }

		/// <summary>
		/// Resets the sprite to initial settings.
		/// </summary>
		public void Reset() {
            frame = 0;
            TotalElapsed = 0f;
        }

		/// <summary>
		/// Pauses and Resets the Sprite.
		/// </summary>
        public void Stop() {
            Pause();
            Reset();
        }
		
    	/// <summary>
		/// Unpauses the Sprite.
		/// </summary>
        public void Play() {
            Paused = false;
        }

		/// <summary>
		/// Pauses the Sprite, so that it will not update the frame on subsequent ticks.
		/// </summary>
		public void Pause() {
            Paused = true;
        }

		/// <summary>
		/// Returns the Asset Name of the Sprite's texture.
		/// </summary>
		public string ImgLoc {
            get { return imgLoc; }
            set { imgLoc = value; }
        }

		/// <summary>
		/// Returns the amount of time for the Sprite to go through one cycle of animation.
		/// </summary>
		public float RunningTime {
			get { return framecount/timePerFrame; }
		}

		/// <summary>
		/// Returns the width of one frame of the Sprite.
		/// </summary>
        public int Width {
            get { return texture.Width/framecount; }
        }
		/// <summary>
		/// Returns the height of the Sprite's texture.
		/// </summary>
		public int Height {
            get { return texture.Height; }
        }

		/// <summary>
		/// Returns the Texture of the Sprite.
		/// </summary>
        public Texture2D Texture {
            get { return texture; }
			set { texture = value; }
        }

		public int Frame {
			get { return frame; }
			set { frame = value; }
		}
    }
}